package git.soulbgm.job;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import git.soulbgm.pojo.NodeDiskFreeRecord;
import git.soulbgm.pojo.NodeUsedInfo;
import git.soulbgm.service.NodeDiskFreeRecordService;
import git.soulbgm.utils.DateUtil;
import git.soulbgm.utils.JsonTool;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static git.soulbgm.common.MonitorConstants.*;

/**
 * 预估磁盘剩余使用时长任务
 *
 * @author SoulBGM
 * @date 2023-08-08
 */
@Service
public class NodeDiskPredictUsageDurationTask {

    private final NodeDiskFreeRecordService service;
    private final StringRedisTemplate redis;

    public NodeDiskPredictUsageDurationTask(NodeDiskFreeRecordService diskFreeRecordService, StringRedisTemplate redis) {
        this.service = diskFreeRecordService;
        this.redis = redis;
    }

    @Scheduled(cron = "${jobs.map.nodeDiskPredictUsageDuration.cron}")
    @SchedulerLock(name = "${jobs.map.nodeDiskPredictUsageDuration.name}", lockAtMostFor = "${jobs.map.nodeDiskPredictUsageDuration.lockAtMostFor}", lockAtLeastFor = "${jobs.map.nodeDiskPredictUsageDuration.lockAtLeastFor}")
    public void execute() throws Exception {
        HashOperations<String, String, String> opsForHash = redis.opsForHash();
        Map<String, String> map = opsForHash.entries(REDIS_KEY_NODE_NOW_USED);
        int size = map.size();
        List<NodeDiskFreeRecord> recordList = new ArrayList<>(size);
        map.forEach((k, v) -> {
            NodeUsedInfo info = JsonTool.parse(v, NodeUsedInfo.class);
            if (info != null) {
                long diskFree = info.getDiskTotal() - info.getDiskUsed();
                recordList.add(new NodeDiskFreeRecord(info.getNodeCode(), info.getHostname(), info.getIp(), info.getDetectionTime(), diskFree));
            }
        });
        Calendar calendar = DateUtil.offsetDate(DateUtil.getCalendar(), DateUtil.DAY, -500);
        service.remove(new LambdaQueryWrapper<NodeDiskFreeRecord>().le(NodeDiskFreeRecord::getDetectionTime, calendar.getTime()));
        boolean result = service.saveBatch(recordList);
        if (result) {
            List<NodeDiskFreeRecord> list = service.list();
            Map<Integer, Integer> predictMap = predictUsageDuration(list);
            Map<String, String> convertMap = convert(predictMap);
            opsForHash.putAll(REDIS_KEY_NODE_DISK_PREDICT, convertMap);
        }
    }

    /**
     * 转换
     *
     * @param predictMap 预计Map
     * @return {@link Map}<{@link String}, {@link String}>
     */
    private Map<String, String> convert(Map<Integer, Integer> predictMap) {
        Map<String, String> convertMap = new HashMap<>(predictMap.size());
        predictMap.forEach((k, v) -> convertMap.put(String.valueOf(k), String.valueOf(v)));
        return convertMap;
    }

    /**
     * 预计使用时长
     *
     * @param list 列表
     * @return {@link Map}<{@link Integer}, {@link Integer}>
     */
    private Map<Integer, Integer> predictUsageDuration(List<NodeDiskFreeRecord> list) {
        Map<Integer, List<NodeDiskFreeRecord>> map = list.stream().collect(Collectors.groupingBy(NodeDiskFreeRecord::getNodeCode));
        Map<Integer, Integer> predictMap = new HashMap<>(map.size());
        map.forEach((k, v) -> {
            List<Long> diffList = new ArrayList<>();
            int size = v.size();
            for (int i = 1; i < size; i++) {
                NodeDiskFreeRecord previous = v.get(i - 1);
                NodeDiskFreeRecord current = v.get(i);
                long diff = previous.getDiskFree() - current.getDiskFree();
                if (diff < 0) {
                    continue;
                }
                diffList.add(diff);
            }
            double avgDiff = diffList.stream().mapToLong(o -> o).average().orElse(0);
            NodeDiskFreeRecord last = v.get(size - 1);
            int duration = (int) (last.getDiskFree() / avgDiff);
            predictMap.put(k, duration);
        });
        return predictMap;
    }

}
